package com.hongshu.web.service.recommendation;

import com.hongshu.web.service.recommendation.model.RecommendContext;
import com.hongshu.web.service.recommendation.model.RankingItem;
import com.hongshu.web.service.recommendation.model.UserProfile;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 重排序服务
 * 实现多样性调整、新鲜度调整、去重过滤、业务规则等重排策略
 *
 * @author hongshu
 * @date 2024/01/01
 */
@Service
@Slf4j
public class RerankService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private DiversityAdjuster diversityAdjuster;
    @Autowired
    private FreshnessAdjuster freshnessAdjuster;
    @Autowired
    private BusinessRuleFilter businessRuleFilter;


    /**
     * 重排序主入口
     */
    public List<String> rerank(List<RankingItem> rankedItems,
                              UserProfile userProfile,
                              RecommendContext context) {
        try {
            log.info("开始重排序，排序后物品数量: {}", rankedItems.size());
            List<String> finalItems = new ArrayList<>();
            // 1. 去重过滤
            List<RankingItem> deduplicatedItems = deduplicateItems(rankedItems, userProfile);
            log.info("去重后物品数量: {}", deduplicatedItems.size());
            // 2. 业务规则过滤
            List<RankingItem> filteredItems = businessRuleFilter.filter(deduplicatedItems, userProfile, context);
            log.info("业务规则过滤后物品数量: {}", filteredItems.size());
            // 3. 多样性调整
            List<RankingItem> diversifiedItems = diversityAdjuster.adjust(filteredItems, userProfile, context);
            log.info("多样性调整后物品数量: {}", diversifiedItems.size());
            // 4. 新鲜度调整
            List<RankingItem> freshItems = freshnessAdjuster.adjust(diversifiedItems, userProfile, context);
            log.info("新鲜度调整后物品数量: {}", freshItems.size());
            // 5. 最终排序
            finalItems = freshItems.stream()
                .sorted((a, b) -> Double.compare(b.getFinalScore(), a.getFinalScore()))
                .map(RankingItem::getItemId)
                .collect(Collectors.toList());
            log.info("重排序完成，最终物品数量: {}", finalItems.size());
            return finalItems;
        } catch (Exception e) {
            log.error("重排序异常", e);
            // 降级到简单排序
            return rankedItems.stream()
                .map(RankingItem::getItemId)
                .collect(Collectors.toList());
        }
    }

    /**
     * 去重过滤
     */
    private List<RankingItem> deduplicateItems(List<RankingItem> items, UserProfile userProfile) {
        Set<String> seenItems = new HashSet<>();
        List<RankingItem> deduplicatedItems = new ArrayList<>();
        for (RankingItem item : items) {
            if (!seenItems.contains(item.getItemId())) {
                seenItems.add(item.getItemId());
                deduplicatedItems.add(item);
            }
        }
        return deduplicatedItems;
    }

    /**
     * 多样性调整器
     */
    @Service
    public static class DiversityAdjuster {

        @Autowired
        private RedisTemplate<String, Object> redisTemplate;


        /**
         * 多样性调整
         */
        public List<RankingItem> adjust(List<RankingItem> items,
                                      UserProfile userProfile,
                                      RecommendContext context) {
            try {
                log.info("开始多样性调整");
                List<RankingItem> diversifiedItems = new ArrayList<>();
                Map<String, Integer> categoryCount = new HashMap<>();
                Map<String, Integer> authorCount = new HashMap<>();
                // 多样性参数
                int maxItemsPerCategory = 3; // 每个类别最多3个物品
                int maxItemsPerAuthor = 2;    // 每个作者最多2个物品
                for (RankingItem item : items) {
                    String itemId = item.getItemId();
                    // 获取物品类别和作者
                    String category = getItemCategory(itemId);
                    String author = getItemAuthor(itemId);
                    // 检查类别多样性
                    int currentCategoryCount = categoryCount.getOrDefault(category, 0);
                    if (currentCategoryCount >= maxItemsPerCategory) {
                        continue; // 跳过该物品
                    }
                    // 检查作者多样性
                    int currentAuthorCount = authorCount.getOrDefault(author, 0);
                    if (currentAuthorCount >= maxItemsPerAuthor) {
                        continue; // 跳过该物品
                    }
                    // 添加物品
                    diversifiedItems.add(item);
                    categoryCount.put(category, currentCategoryCount + 1);
                    authorCount.put(author, currentAuthorCount + 1);
                }

                log.info("多样性调整完成，调整后物品数量: {}", diversifiedItems.size());
                return diversifiedItems;
            } catch (Exception e) {
                log.error("多样性调整异常", e);
                return items; // 返回原始列表
            }
        }

        /**
         * 获取物品类别
         */
        private String getItemCategory(String itemId) {
            String key = "item_category:" + itemId;
            String category = (String) redisTemplate.opsForValue().get(key);
            return category != null ? category : "unknown";
        }

        /**
         * 获取物品作者
         */
        private String getItemAuthor(String itemId) {
            String key = "item_author:" + itemId;
            String author = (String) redisTemplate.opsForValue().get(key);
            return author != null ? author : "unknown";
        }
    }

    /**
     * 新鲜度调整器
     */
    @Service
    public static class FreshnessAdjuster {

        @Autowired
        private RedisTemplate<String, Object> redisTemplate;


        /**
         * 新鲜度调整
         */
        public List<RankingItem> adjust(List<RankingItem> items,
                                     UserProfile userProfile,
                                     RecommendContext context) {
            try {
                log.info("开始新鲜度调整");
                List<RankingItem> freshItems = new ArrayList<>();
                long currentTime = System.currentTimeMillis();
                for (RankingItem item : items) {
                    String itemId = item.getItemId();
                    // 获取物品创建时间
                    long createTime = getItemCreateTime(itemId);
                    // 计算新鲜度分数
                    double freshnessScore = calculateFreshnessScore(createTime, currentTime);
                    // 调整最终分数
                    double adjustedScore = item.getFinalScore() * (0.7 + 0.3 * freshnessScore);
                    RankingItem adjustedItem = RankingItem.builder()
                        .itemId(itemId)
                        .ctrScore(item.getCtrScore())
                        .durationScore(item.getDurationScore())
                        .interactionScore(item.getInteractionScore())
                        .finalScore(adjustedScore)
                        .build();
                    freshItems.add(adjustedItem);
                }
                // 按调整后的分数重新排序
                freshItems.sort((a, b) -> Double.compare(b.getFinalScore(), a.getFinalScore()));
                log.info("新鲜度调整完成，调整后物品数量: {}", freshItems.size());
                return freshItems;
            } catch (Exception e) {
                log.error("新鲜度调整异常", e);
                return items; // 返回原始列表
            }
        }

        /**
         * 计算新鲜度分数
         */
        private double calculateFreshnessScore(long createTime, long currentTime) {
            long timeDiff = currentTime - createTime;
            // 时间差转换为小时
            double hoursDiff = timeDiff / (1000.0 * 60 * 60);
            // 新鲜度衰减函数
            if (hoursDiff <= 1) {
                return 1.0; // 1小时内，新鲜度满分
            } else if (hoursDiff <= 24) {
                return 0.8; // 1天内，新鲜度较高
            } else if (hoursDiff <= 168) { // 1周
                return 0.6; // 1周内，新鲜度中等
            } else if (hoursDiff <= 720) { // 1个月
                return 0.4; // 1个月内，新鲜度较低
            } else {
                return 0.2; // 1个月以上，新鲜度很低
            }
        }

        /**
         * 获取物品创建时间
         */
        private long getItemCreateTime(String itemId) {
            String key = "item_create_time:" + itemId;
            Long createTime = (Long) redisTemplate.opsForValue().get(key);
            return createTime != null ? createTime : System.currentTimeMillis();
        }
    }

    /**
     * 业务规则过滤器
     */
    @Service
    public static class BusinessRuleFilter {

        @Autowired
        private RedisTemplate<String, Object> redisTemplate;


        /**
         * 业务规则过滤
         */
        public List<RankingItem> filter(List<RankingItem> items,
                                      UserProfile userProfile,
                                      RecommendContext context) {
            try {
                log.info("开始业务规则过滤");
                List<RankingItem> filteredItems = new ArrayList<>();
                for (RankingItem item : items) {
                    String itemId = item.getItemId();
                    // 1. 内容审核过滤
                    if (!isContentApproved(itemId)) {
                        continue;
                    }
                    // 2. 用户权限过滤
                    if (!hasUserPermission(itemId, userProfile)) {
                        continue;
                    }
                    // 3. 地域限制过滤
                    if (!isLocationAllowed(itemId, userProfile)) {
                        continue;
                    }
                    // 4. 年龄限制过滤
                    if (!isAgeAllowed(itemId, userProfile)) {
                        continue;
                    }
                    // 5. 黑名单过滤
                    if (isInBlacklist(itemId, userProfile)) {
                        continue;
                    }
                    filteredItems.add(item);
                }
                log.info("业务规则过滤完成，过滤后物品数量: {}", filteredItems.size());
                return filteredItems;
            } catch (Exception e) {
                log.error("业务规则过滤异常", e);
                return items; // 返回原始列表
            }
        }

        /**
         * 检查内容是否已审核
         */
        private boolean isContentApproved(String itemId) {
            String key = "content_approval:" + itemId;
            String status = (String) redisTemplate.opsForValue().get(key);
            return "approved".equals(status);
        }

        /**
         * 检查用户权限
         */
        private boolean hasUserPermission(String itemId, UserProfile userProfile) {
            // 检查用户是否有权限访问该内容
            String key = "item_permission:" + itemId;
            @SuppressWarnings("unchecked")
            List<String> allowedUsers = (List<String>) redisTemplate.opsForValue().get(key);
            if (allowedUsers == null || allowedUsers.isEmpty()) {
                return true; // 无限制
            }
            return allowedUsers.contains(userProfile.getUserId());
        }

        /**
         * 检查地域限制
         */
        private boolean isLocationAllowed(String itemId, UserProfile userProfile) {
            String key = "item_location:" + itemId;
            @SuppressWarnings("unchecked")
            List<String> allowedLocations = (List<String>) redisTemplate.opsForValue().get(key);
            if (allowedLocations == null || allowedLocations.isEmpty()) {
                return true; // 无地域限制
            }
            String userLocation = userProfile.getLocation();
            if (userLocation == null) {
                return true; // 用户位置未知，允许访问
            }
            return allowedLocations.contains(userLocation);
        }

        /**
         * 检查年龄限制
         */
        private boolean isAgeAllowed(String itemId, UserProfile userProfile) {
            String key = "item_age_limit:" + itemId;
            Integer ageLimit = (Integer) redisTemplate.opsForValue().get(key);
            if (ageLimit == null) {
                return true; // 无年龄限制
            }
            Integer userAge = userProfile.getAge();
            if (userAge == null) {
                return true; // 用户年龄未知，允许访问
            }
            return userAge >= ageLimit;
        }

        /**
         * 检查黑名单
         */
        private boolean isInBlacklist(String itemId, UserProfile userProfile) {
            String key = "user_blacklist:" + userProfile.getUserId();
            @SuppressWarnings("unchecked")
            List<String> blacklist = (List<String>) redisTemplate.opsForValue().get(key);
            if (blacklist == null) {
                return false; // 无黑名单
            }
            return blacklist.contains(itemId);
        }
    }
}

